/*--------------------------------------------- Noble Steed Engine-------------------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 7 2013

File:
	nstexture.cpp

Description:
	This file contains the definition for the NSTexture class functions along with any other helper
	functions
*-----------------------------------------------------------------------------------------------------*/


#include <nstexture.h>
#include <IL/il.h>
#include <IL/ilu.h>
#include <logfile.h>
#include <sstream>
#include <SOIL.h>

NSTexture::NSTexture(MapType mapT): 
	mapType(mapT),
	NumTexTypes(NUM_TEX_TYPES)
{
	LogFile("Creating texture...");
	texIDMap[Tex1D] = TextureTarget();
	texIDMap[Tex2D] = TextureTarget();
	texIDMap[Tex3D] = TextureTarget();
	texIDMap[CubeMap] = TextureTarget();
}

NSTexture::~NSTexture()
{
	std::map<TexType, TextureTarget>::iterator iter = texIDMap.begin();
	while ( iter != texIDMap.end() )
	{
		std::stringstream ss;
		if (iter->second.texID != 0)
		{
			ss << "Deleting texture with TexType ID " << iter->first;
			LogFile l(ss);
			glDeleteTextures(1,&iter->second.texID);
			LogFile("Texture Deleted");
		}
		++iter;
	}
}

void NSTexture::disable(TexType type)
{
	glActiveTexture(BASE_TEX_UNIT + mapType);
	glBindTexture(type, 0);
}

std::string NSTexture::getFilename(TexType type) const
{
	if (type == CubeMap)
	{
		LogFile("Cannot get cubemap filename using this function - please use getCubemapFilename");
		return "";
	}
	if (!texIDMap.at(type).fnames.empty())
		return texIDMap.at(type).fnames.back();
	LogFile("Unable to get texture filename - none found");
	return "";
}

std::string NSTexture::getCubemapFilename(CubemapFace face) const
{
	if (!texIDMap.at(CubeMap).fnames.empty())
	{
		if (face >= texIDMap.at(CubeMap).fnames.size())
		{
			LogFile("Unable to get filename for cubemap face - index out of bounds");
			return "";
		}
		return texIDMap.at(CubeMap).fnames[face];
	}

	LogFile("Unable to get cubemap texture filename - none found");
	return "";
}

bool NSTexture::loadFromFile(const std::string & fileName, 
	const std::string & textureDir, 
	bool mipmap, 
	TexType type)
{
	std::string fullPath;

	if (!textureDir.empty())
		fullPath = textureDir + "/" + fileName;
	else
		fullPath = fileName;

	TextureTarget & t = texIDMap[type];

	t.fnames.push_back(fileName);
	t.mipMapped = mipmap;
	textureDirectory = textureDir;
	ilInit();
	iluInit();
	ILuint imageID;
	ilGenImages(1, &imageID);
	ilBindImage(imageID);
	int worked = ilLoadImage(fullPath.c_str());
	int converted = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
	if (!worked || !converted)
	{
		if (!worked)
		{
			LogFile("Could not load image with name : " + t.fnames.back() );
			ilDeleteImages(1, &imageID);
			return false;
		}
		LogFile("Could not convert image to specified format - check texture load source code ");
		ilDeleteImages(1, &imageID);
		return false;
	}
	LogFile("Successfully loaded texture with filename " + t.fnames.back() );

	glGenTextures(1, &(t.texID));
	glBindTexture(type, t.texID);

	glTexImage2D(type, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0,
		ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());

	if (t.mipMapped)
	{
		glGenerateMipmap(type);
		glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(type, GL_TEXTURE_MAX_LEVEL, 7);
	}
	else
		glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glBindTexture(type,0);
	ilDeleteImages(1, &imageID);

	return true;
}

bool NSTexture::loadCubemapFromFile(const std::string & _xPlus, 
		const std::string & _xMinus, 
		const std::string & _yPlus,
		const std::string & _yMinus,
		const std::string & _zPlus,
		const std::string & _zMinus,
		const std::string & textureDir,
		bool mipmap)
{
	std::string fullPath;
	textureDirectory = textureDir;

	TextureTarget * t = &texIDMap[CubeMap];

	t->fnames.resize(NumCubemapFaces);

	t->fnames[xPlus] = _xPlus; t->fnames[xMinus] = _xMinus;
	t->fnames[yPlus] = _yPlus; t->fnames[yMinus] = _yMinus;
	t->fnames[zPlus] = _zPlus; t->fnames[zMinus] = _zMinus;
	t->mipMapped = mipmap;

	ilInit();
	iluInit();

	glGenTextures(1, &t->texID);
	glBindTexture(CubeMap, t->texID);

	for ( int i = 0; i < NumCubemapFaces; ++i)
	{
		CubemapFace face = (CubemapFace)i;

		std::string fullPath;

		if (!textureDir.empty())
			fullPath = textureDir + "/" + t->fnames[face];
		else
			fullPath = t->fnames[face];

		ILuint imageID;
		ilGenImages(1, &imageID);
		ilBindImage(imageID);
		int worked = ilLoadImage(fullPath.c_str());
		int converted = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
		if (!worked || !converted)
		{
			if (!worked)
			{
				LogFile("Could not load image for cubemap face with name : " + t->fnames[face] );
				ilDeleteImages(1, &imageID);
				return false;
			}
			LogFile("Could not convert image to specified format - check texture load source code ");
			ilDeleteImages(1, &imageID);
			return false;
		}
		LogFile("Successfully loaded cubemap image with filename " + t->fnames[face] );

		glTexImage2D(BASE_CUBEMAP_FACE + face, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());

		ilDeleteImages(1, &imageID);
	}

	if (t->mipMapped)
	{
		glGenerateMipmap(CubeMap);
		glTexParameteri(CubeMap, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(CubeMap, GL_TEXTURE_MAX_LEVEL, 7);
	}
	else
		glTexParameteri(CubeMap, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameteri(CubeMap, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(CubeMap, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(CubeMap, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(CubeMap, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glBindTexture(CubeMap,0);

	return true;
}

bool NSTexture::loadCubemapFromFile(const std::string & cubeMapFile, 
	const std::string & textureDir,
	bool mipmap)
{
	std::string fullPath;

	if (!textureDir.empty())
		fullPath = textureDir + "/" + cubeMapFile;
	else
		fullPath = cubeMapFile;

	TextureTarget & t = texIDMap[CubeMap];

	t.fnames.push_back(cubeMapFile);
	t.mipMapped = mipmap;
	textureDirectory = textureDir;

	t.texID = SOIL_load_OGL_single_cubemap(fullPath.c_str(), SOIL_DDS_CUBEMAP_FACE_ORDER, 
		SOIL_LOAD_AUTO,
		SOIL_CREATE_NEW_ID,
		SOIL_FLAG_MIPMAPS);
	return true;
}

void NSTexture::use(TexType type)
{
	glActiveTexture(BASE_TEX_UNIT + mapType);
	glBindTexture(type, texIDMap[type].texID);
}


NSTexture::TextureTarget::TextureTarget(): 
	texID(0),
	fnames(std::vector<std::string>()),
	mipMapped(false)
{}